#include <stdarg.h>
#include "types.h"
#include "riscv.h"
#include "defs.h"

static char digits[] = "0123456789ABCDEF";

//编译器会将类似printf("%s\n", str);优化为puts(str);
int puts(const char* str) {
    if(str == 0) {
        consolewrite("(null)", 6);
        return 0;
    }
    for(; *str; str++) {
        consputc(*str);
    }
    return 0;
}


int putchar(int c) {
    consputc(c);
    return 0;
}

void
printint(long long xx, int base, int sign) {
    unsigned long long x;
    //20位刚好容纳最大的10进制64位数的字符串表示（包括符号位）
    char buf[20];

    //这里的if语句做了两件事：判断是否是有符号数，如果是，那么后续还是否需要处理符号
    //即便是有符号数，如果传入的值是正数，那么后续也不需要处理符号，故对sign进行赋值
    if(sign && (sign = xx < 0))
        x = -xx;
    else
        x = xx;

    int i = 0;
    //base表示进制数，这里根据进制数将数字逆序转换为字符串
    //123->"321"
    do {
        buf[i++] = digits[x % base];
        x /= base;
    } while(x != 0);

    if(sign)
        buf[i++] = '-';
    //buf中存储的是逆序的字符串，这里将其正序输出
    while(--i >= 0)
        consputc(buf[i]);
}

void 
printptr(uint64 x) {
    int i;
    consputc('0');
    consputc('x');
    // 一个指针共有64位，8个字节，一个字节需要两个十六进制字符表示
    // 故一共需要输出16个字符，即16次循环
    for(i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) {
        // 每次循环x左移4位，故右移60位取出最高的4位
        char c = digits[x >> (sizeof(uint64) * 8 - 4)];
        consputc(c);
    }
}

int 
printf(const char *fmt, ...) {
    //stdarg处理可变参数
    va_list ap;
    int i, cx, c0, c1, c2;
    char *s;

    va_start(ap, fmt);

    //与0xff取位与是防御性编程，防止转成int后高位污染
    for(i = 0; (cx = fmt[i] & 0xff) != 0; i++) {
        if(cx != '%') {
            consputc(cx);
            continue;
        }

        i++;
        c0 = c1 = c2 = 0;
        c0 = fmt[i+0] & 0xff;
        if(c0) c1 = fmt[i+1] & 0xff;
        if(c1) c2 = fmt[i+2] & 0xff;

        //解析格式化
        if(c0 == 'd') {
            printint(va_arg(ap, int), 10, 1);
        }
        else if(c0 == 'l' && c1 == 'd') {
            printint(va_arg(ap, uint64), 10, 1);
            i += 1;
        }
        else if(c0 == 'l' && c1 == 'l' && c2 == 'd') {
            printint(va_arg(ap, uint64), 10, 1);
            i += 2;
        }
        else if(c0 == 'x') {
            printint(va_arg(ap, uint32), 16, 0);
        }
        else if(c0 == 'l' && c1 == 'x') {
            printint(va_arg(ap, uint64), 16, 0);
            i += 1;
        }
        else if(c0 == 'l' && c1 == 'l' && c2 == 'x') {
            printint(va_arg(ap, uint64), 16, 0);
            i += 2;
        }
        else if(c0 == 'u') {
            printint(va_arg(ap, uint32), 10, 0);
        }
        else if(c0 == 'l' && c1 == 'u') {
            printint(va_arg(ap, uint64), 10, 0);
            i += 1;
        }
        else if(c0 == 'l' && c1 == 'l' && c2 == 'u') {
            printint(va_arg(ap, uint64), 10, 0);
            i += 2;
        }
        else if(c0 == 'p') {
            printptr(va_arg(ap, uint64));
        }
        else if(c0 == 'c') {
            consputc(va_arg(ap, uint));
        }
        else if(c0 =='s') {
            if((s = va_arg(ap, char*)) == 0)
                s = "(null)";
            for(; *s; s++)
                consputc(*s);
        }
        else if(c0 == '%'){
            consputc('%');
        } 
        else if(c0 == 0){
            break;
        } 
        else {
            consputc('%');
            consputc(c0);
        }
    }

    va_end(ap);
    return 0;
}